{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "e3fbccdc-0d7c-45ef-8588-20d13a44a676",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然可以！来一个轻松的狗熊笑话：\\n\\n---\\n\\n有一天，一只狗熊走进了理发店，对理发师说：“师傅，给我理个发！”\\n\\n理发师有点紧张，但还是点了点头，问：“您想要什么发型？”\\n\\n狗熊说：“很简单，上面剃光，两边也剃光，中间留一撮毛。”\\n\\n理发师奇怪地问：“中间留一撮？这是什么造型啊？”\\n\\n狗熊叹了口气，说：“唉，别提了，我这叫‘一心一意’——上面一心，中间一意，专心找蜂蜜！”\\n\\n---\\n\\n希望这个笑话让你笑一笑 😄  \\n还想听更多狗熊的笑话吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 142, 'prompt_tokens': 20, 'total_tokens': 162, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-bb8852aa-8f52-9c12-bde4-35d18d9893a2', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--7b33edb9-befd-4ecc-84fd-13fca824afee-0', usage_metadata={'input_tokens': 20, 'output_tokens': 142, 'total_tokens': 162, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"给我讲一个关于{foo}的笑话\")\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "chain = prompt | model\n",
    "chain.invoke({\"foo\": \"狗熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "9f8274ba-663c-4299-a633-2d1a604dee92",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然可以！来一个轻松有趣的关于狗熊的笑话：', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 14, 'prompt_tokens': 20, 'total_tokens': 34, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-e3e6d3a9-8597-963a-876c-64e18992343b', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--e107a9fa-2b12-4bd1-80f6-3d079786d839-0', usage_metadata={'input_tokens': 20, 'output_tokens': 14, 'total_tokens': 34, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 自定义停止输出符\n",
    "chain = prompt | model.bind(stop=[\"\\n\"])\n",
    "chain.invoke({\"foo\": \"狗熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "09628a45-2333-4b1b-8095-c50f02e1a770",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"setup\": \"为什么男人喜欢玩电脑游戏？\", \"punchline\": \"因为在游戏中，他们可以成为任何人，除了家务。\"}', 'name': 'joke'}, 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 34, 'prompt_tokens': 196, 'total_tokens': 230, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-da5ee7ef-bba9-9c9e-b01c-6d87a45d354d', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--b9b65820-ab07-46d1-b2e3-42c725237224-0', usage_metadata={'input_tokens': 196, 'output_tokens': 34, 'total_tokens': 230, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 兼容openai函数调用的方式\n",
    "functions = [\n",
    "    {\n",
    "        \"name\": \"joke\",\n",
    "        \"description\": \"讲笑话\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"setup\": {\"type\": \"string\", \"description\": \"笑话的开头\"},\n",
    "                \"punchline\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"爆梗的结尾\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\"setup\", \"punchline\"],\n",
    "        },\n",
    "    }\n",
    "]\n",
    "chain = prompt | model.bind(function_call={\"name\": \"joke\"}, functions=functions)\n",
    "chain.invoke({\"foo\": \"男人\"}, config={})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "f64b4cdd-a507-4a18-9d5d-d5a97a1f482d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然可以！这里有一个轻松幽默的笑话：\\n\\n---\\n\\n有一天，一位女士去超市买东西，走到收银台时，发现队伍排得很长。\\n\\n她有点着急，就问旁边的人：“这个队伍要排多久啊？”\\n\\n旁边的男士看了看手表，说：“大概要15分钟吧。”\\n\\n女士皱了皱眉，说：“真慢，我要是带了书来就好了，还能看看。”\\n\\n男士微微一笑，说：“其实我带了。”\\n\\n女士一愣，接着笑了：“你带了？那你坐哪儿了？”\\n\\n男士淡定地回答：“坐在你旁边的位置上。”\\n\\n---\\n\\n希望这个笑话能让你笑一笑 😊\\n\\n如果你想要不同风格的笑话，也可以告诉我！'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 输出解析器\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "chain = prompt | model | StrOutputParser()\n",
    "chain.invoke({\"foo\": \"女人\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "f85b35aa-771f-4cc3-8ab2-20c96edd790a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'setup': '我跟老婆说我想看喜剧', 'punchline': '她说她已经把冰箱清空了，让我看她的身材'}"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 与函数调用混合使用\n",
    "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n",
    "\n",
    "chain = (\n",
    "    prompt\n",
    "    | model.bind(function_call={\"name\": \"joke\"}, functions=functions)\n",
    "    | JsonOutputFunctionsParser()\n",
    ")\n",
    "\n",
    "chain.invoke({\"foo\": \"女人\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "9bfe8abf-7166-4e2b-b6e5-d4f4586adf03",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'因为她们觉得每个包包都有一个故事，而她们就是那个故事的主角。'"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#只输出setup\n",
    "from langchain.output_parsers.openai_functions import JsonKeyOutputFunctionsParser\n",
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "chain = (\n",
    "    {\"foo\": RunnablePassthrough()} #使用RunnablePassthrough()跳过prompt\n",
    "    | prompt\n",
    "    | model.bind(function_call={\"name\": \"joke\"}, functions=functions)\n",
    "    | JsonKeyOutputFunctionsParser(key_name=\"punchline\") # 定义输出的key\n",
    ")\n",
    "\n",
    "# chain.invoke({\"foo\": \"女人\"})\n",
    "#使用RunnablePassthrough()跳过prompt\n",
    "chain.invoke(\"女人\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "23d4f8e7-16ca-405f-aa76-599bc37e2bba",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'马化腾来自**中国广东省汕头市**。  \\n他是腾讯公司的创始人之一，出生于1971年，籍贯为广东汕头。尽管他在深圳创业并创立了腾讯（1998年于深圳），但他的家乡和家族根源在汕头。'"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用Runnables来连接多链结构\n",
    "from operator import itemgetter #获取可迭代对象中指定索引或键对应的元素\n",
    "\n",
    "from langchain.schema import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt1 = ChatPromptTemplate.from_template(\"{person}来自于哪个城市?\")\n",
    "prompt2 = ChatPromptTemplate.from_template(\n",
    "    \"{city}属于哪个省? 用{language}来回答\"\n",
    ")\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "\n",
    "chain1 = prompt1 | model | StrOutputParser()\n",
    "\n",
    "chain2 = (\n",
    "    {\"city\": chain1, \"language\": itemgetter(\"language\")} #获取invoke中的language\n",
    "    | prompt2\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")\n",
    "chain1.invoke({\"person\": \"马化腾\"})\n",
    "# chain2.invoke({\"person\": \"马化腾\", \"language\": \"中文\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "3786e069-b7f1-4c09-a879-5da0d1bcc19c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptValue(messages=[HumanMessage(content='有这个颜色的水果是火龙果,有这个颜色的国旗是中国？', additional_kwargs={}, response_metadata={})])"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "prompt1 = ChatPromptTemplate.from_template(\n",
    "    \"生成一个{attribute}属性的颜色。除了返回这个颜色的名字不要做其他事:\"\n",
    ")\n",
    "prompt2 = ChatPromptTemplate.from_template(\n",
    "    \"什么水果是这个颜色:{color},只返回这个水果的名字不要做其他事情:\"\n",
    ")\n",
    "prompt3 = ChatPromptTemplate.from_template(\n",
    "    \"哪个国家的国旗有这个颜色:{color},只返回这个国家的名字不要做其他事情:\"\n",
    ")\n",
    "prompt4 = ChatPromptTemplate.from_template(\n",
    "    \"有这个颜色的水果是{fruit},有这个颜色的国旗是{country}？\"\n",
    ")\n",
    "\n",
    "model_parser = model | StrOutputParser()\n",
    "# 生成一个颜色\n",
    "color_generator = (\n",
    "    {\"attribute\": RunnablePassthrough()} | prompt1 | {\"color\": model_parser}\n",
    ")\n",
    "color_to_fruit = prompt2 | model_parser\n",
    "color_to_country = prompt3 | model_parser\n",
    "question_generator = (\n",
    "    color_generator | {\"fruit\": color_to_fruit, \"country\": color_to_country} | prompt4\n",
    ")\n",
    "question_generator.invoke(\"强烈的\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "2ae224e2-fcc1-4161-b97b-a8b83772e27b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'根据你提供的**积极**与**消极**评论，以下是关于“**生孩子不仅是个人选择，也对社会的可持续发展和人类的未来具有重要意义**”这一论点的**全面总结与最终回复**，旨在呈现一个**理性、平衡、有深度的回应**：\\n\\n---\\n\\n## ✅ **最终回复：生育议题的多维视角与平衡思考**\\n\\n生育，作为人类繁衍与社会延续的重要行为，确实不仅是个人选择，也与社会结构、文化传承、经济发展乃至人类未来息息相关。然而，在肯定其积极意义的同时，我们也必须正视其背后的复杂性与潜在风险。唯有在尊重个体自由与推动社会可持续发展之间找到平衡，才能构建真正健康、包容和可持续的未来。\\n\\n---\\n\\n### 🌱 一、生育的积极意义：社会发展的基石\\n\\n1. **维持社会基本运转与劳动力供给**  \\n   稳定的人口结构是经济、教育、医疗等系统持续运行的基础。新生人口为社会提供未来的生产力与消费力，有助于维持社会活力与稳定。\\n\\n2. **文化传承与价值延续**  \\n   孩子是语言、传统、价值观的重要载体，有助于增强社会的文化认同与凝聚力，促进代际之间的联系与情感纽带。\\n\\n3. **应对人口老龄化**  \\n   在全球许多国家面临老龄化趋势的背景下，适度生育有助于缓解养老、医疗体系压力，减轻年轻一代的负担，保障社会保障制度的可持续性。\\n\\n4. **个体成长与家庭幸福**  \\n   对许多人而言，养育孩子是实现人生价值、增强家庭幸福感的重要途径，有助于提升责任感、同理心与情感联结。\\n\\n5. **人力资源储备与未来挑战应对**  \\n   新生人口为未来社会提供多样化的人才资源，有助于应对科技进步、气候变化等全球性挑战。\\n\\n---\\n\\n### ⚠️ 二、潜在风险与局限：不能忽视的现实问题\\n\\n1. **忽视资源分配与人口质量**  \\n   单纯强调“人口增长”可能掩盖教育、医疗、就业等资源分配不均的问题。一个社会的可持续发展，不应只靠“数量”，更应注重“质量”。\\n\\n2. **个体自由与身体自主权的挑战**  \\n   将生育视为“社会责任”可能带来舆论压力甚至政策干预，影响个体（尤其是女性）的自主选择权与人生规划。\\n\\n3. **环境与生态压力**  \\n   在全球面临气候变化、资源紧张的背景下，鼓励生育可能加剧环境负担，违背可持续发展的初衷。\\n\\n4. **文化保守主义的风险**  \\n   将生育与文化传承绑定，可能被某些群体利用来推动文化同质化、排斥多元文化，阻碍社会的包容性发展。\\n\\n5. **育儿成本与现实困境**  \\n   在许多国家，育儿成本高昂、职场对女性不友好、托育资源不足等问题严重，鼓励生育可能加重家庭负担，反而降低生活质量。\\n\\n6. **技术替代与未来趋势的忽视**  \\n   未来社会的发展可能更多依赖人工智能、自动化等技术手段，而非单纯依赖人口增长来解决劳动力短缺问题。\\n\\n7. **幸福定义的狭隘化**  \\n   将生育视为“人生必经之路”或“幸福的唯一形式”，可能造成对无子女家庭、单身者、丁克群体的误解与歧视。\\n\\n---\\n\\n### 🧭 三、结论与建议：在尊重与责任之间找到平衡\\n\\n> **生育，是一种权利，也是一种选择，同时也可以是社会发展的助力。**\\n\\n我们应当认识到生育的社会价值，但更重要的是：\\n\\n- **尊重个体选择**：无论是否生育，都应被社会尊重和平等对待。\\n- **优化支持体系**：政府与社会应提供更完善的育儿福利、性别平等政策、职场支持和教育资源，降低育儿压力。\\n- **推动技术与制度创新**：通过科技手段（如AI、自动化）和制度改革（如弹性退休、人才引进）来弥补人口结构变化带来的挑战。\\n- **关注生态与可持续发展**：在鼓励生育的同时，必须重视资源合理利用与环境保护，避免人口增长对地球造成不可逆的影响。\\n- **促进文化多样性**：鼓励多元家庭模式和价值观共存，避免将生育与文化认同简单绑定。\\n\\n---\\n\\n### 📌 总结一句话：\\n\\n> **生育既是个人自由，也关乎社会未来。尊重选择、优化支持、平衡发展，才是推动个体幸福与社会进步的真正路径。**\\n\\n---\\n\\n如需将此内容用于**论文写作、辩论准备、政策建议或公众演讲**，也可根据具体场景进行调整与扩展。欢迎继续提问，我将为你提供定制化支持！'"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"多链执行与结果合并：\n",
    "      输入\n",
    "      / \\\n",
    "     /   \\\n",
    " 分支1   分支2\n",
    "     \\   /\n",
    "      \\ /\n",
    "    合并结果\n",
    "\"\"\"\n",
    "# 唯物辩证链\n",
    "planner = (\n",
    "    ChatPromptTemplate.from_template(\"生成一个关于{input}的论点\")\n",
    "    | ChatOpenAI(\n",
    "        model=\"qwen-plus\",\n",
    "        temperature=0,\n",
    "        openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "        openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "        )\n",
    "    | StrOutputParser()\n",
    "    | {\"base_response\": RunnablePassthrough()}\n",
    ")\n",
    "\n",
    "arguments_for = (\n",
    "    ChatPromptTemplate.from_template(\n",
    "        \"列出以下内容的优点或积极方面:{base_response}\"\n",
    "    )\n",
    "    | ChatOpenAI(\n",
    "        model=\"qwen-plus\",\n",
    "        temperature=0,\n",
    "        openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "        openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "        )\n",
    "    | StrOutputParser()\n",
    ")\n",
    "arguments_against = (\n",
    "    ChatPromptTemplate.from_template(\n",
    "        \"列出以下内容的缺点或消极方面:{base_response}\"\n",
    "    )\n",
    "    | ChatOpenAI(\n",
    "        model=\"qwen-plus\",\n",
    "        temperature=0,\n",
    "        openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "        openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "        )\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "final_responder = (\n",
    "    ChatPromptTemplate.from_messages(\n",
    "        [\n",
    "            (\"ai\", \"{original_response}\"),\n",
    "            (\"human\", \"积极:\\n{results_1}\\n\\n消极:\\n{results_2}\"),\n",
    "            (\"system\", \"根据评论生成最终的回复\"),\n",
    "        ]\n",
    "    )\n",
    "    | ChatOpenAI(\n",
    "        model=\"qwen-plus\",\n",
    "        temperature=0,\n",
    "        openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "        openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "        )\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "chain = (\n",
    "    planner\n",
    "    | {\n",
    "        \"results_1\": arguments_for,\n",
    "        \"results_2\": arguments_against,\n",
    "        \"original_response\": itemgetter(\"base_response\"),\n",
    "    }\n",
    "    | final_responder\n",
    ")\n",
    "\n",
    "chain.invoke({\"input\": \"生孩子\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "e4c5cb34-128d-45b3-87cc-36fe0924f661",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\\nCREATE TABLE \"Album\" (\\n\\t\"AlbumId\" INTEGER NOT NULL, \\n\\t\"Title\" NVARCHAR(160) NOT NULL, \\n\\t\"ArtistId\" INTEGER NOT NULL, \\n\\tPRIMARY KEY (\"AlbumId\"), \\n\\tFOREIGN KEY(\"ArtistId\") REFERENCES \"Artist\" (\"ArtistId\")\\n)\\n\\n/*\\n3 rows from Album table:\\nAlbumId\\tTitle\\tArtistId\\n1\\tFor Those About To Rock We Salute You\\t1\\n2\\tBalls to the Wall\\t2\\n3\\tRestless and Wild\\t2\\n*/\\n\\n\\nCREATE TABLE \"Artist\" (\\n\\t\"ArtistId\" INTEGER NOT NULL, \\n\\t\"Name\" NVARCHAR(120), \\n\\tPRIMARY KEY (\"ArtistId\")\\n)\\n\\n/*\\n3 rows from Artist table:\\nArtistId\\tName\\n1\\tAC/DC\\n2\\tAccept\\n3\\tAerosmith\\n*/\\n\\n\\nCREATE TABLE \"Customer\" (\\n\\t\"CustomerId\" INTEGER NOT NULL, \\n\\t\"FirstName\" NVARCHAR(40) NOT NULL, \\n\\t\"LastName\" NVARCHAR(20) NOT NULL, \\n\\t\"Company\" NVARCHAR(80), \\n\\t\"Address\" NVARCHAR(70), \\n\\t\"City\" NVARCHAR(40), \\n\\t\"State\" NVARCHAR(40), \\n\\t\"Country\" NVARCHAR(40), \\n\\t\"PostalCode\" NVARCHAR(10), \\n\\t\"Phone\" NVARCHAR(24), \\n\\t\"Fax\" NVARCHAR(24), \\n\\t\"Email\" NVARCHAR(60) NOT NULL, \\n\\t\"SupportRepId\" INTEGER, \\n\\tPRIMARY KEY (\"CustomerId\"), \\n\\tFOREIGN KEY(\"SupportRepId\") REFERENCES \"Employee\" (\"EmployeeId\")\\n)\\n\\n/*\\n3 rows from Customer table:\\nCustomerId\\tFirstName\\tLastName\\tCompany\\tAddress\\tCity\\tState\\tCountry\\tPostalCode\\tPhone\\tFax\\tEmail\\tSupportRepId\\n1\\tLuís\\tGonçalves\\tEmbraer - Empresa Brasileira de Aeronáutica S.A.\\tAv. Brigadeiro Faria Lima, 2170\\tSão José dos Campos\\tSP\\tBrazil\\t12227-000\\t+55 (12) 3923-5555\\t+55 (12) 3923-5566\\tluisg@embraer.com.br\\t3\\n2\\tLeonie\\tKöhler\\tNone\\tTheodor-Heuss-Straße 34\\tStuttgart\\tNone\\tGermany\\t70174\\t+49 0711 2842222\\tNone\\tleonekohler@surfeu.de\\t5\\n3\\tFrançois\\tTremblay\\tNone\\t1498 rue Bélanger\\tMontréal\\tQC\\tCanada\\tH2G 1A7\\t+1 (514) 721-4711\\tNone\\tftremblay@gmail.com\\t3\\n*/\\n\\n\\nCREATE TABLE \"Employee\" (\\n\\t\"EmployeeId\" INTEGER NOT NULL, \\n\\t\"LastName\" NVARCHAR(20) NOT NULL, \\n\\t\"FirstName\" NVARCHAR(20) NOT NULL, \\n\\t\"Title\" NVARCHAR(30), \\n\\t\"ReportsTo\" INTEGER, \\n\\t\"BirthDate\" DATETIME, \\n\\t\"HireDate\" DATETIME, \\n\\t\"Address\" NVARCHAR(70), \\n\\t\"City\" NVARCHAR(40), \\n\\t\"State\" NVARCHAR(40), \\n\\t\"Country\" NVARCHAR(40), \\n\\t\"PostalCode\" NVARCHAR(10), \\n\\t\"Phone\" NVARCHAR(24), \\n\\t\"Fax\" NVARCHAR(24), \\n\\t\"Email\" NVARCHAR(60), \\n\\tPRIMARY KEY (\"EmployeeId\"), \\n\\tFOREIGN KEY(\"ReportsTo\") REFERENCES \"Employee\" (\"EmployeeId\")\\n)\\n\\n/*\\n3 rows from Employee table:\\nEmployeeId\\tLastName\\tFirstName\\tTitle\\tReportsTo\\tBirthDate\\tHireDate\\tAddress\\tCity\\tState\\tCountry\\tPostalCode\\tPhone\\tFax\\tEmail\\n1\\tAdams\\tAndrew\\tGeneral Manager\\tNone\\t1962-02-18 00:00:00\\t2002-08-14 00:00:00\\t11120 Jasper Ave NW\\tEdmonton\\tAB\\tCanada\\tT5K 2N1\\t+1 (780) 428-9482\\t+1 (780) 428-3457\\tandrew@chinookcorp.com\\n2\\tEdwards\\tNancy\\tSales Manager\\t1\\t1958-12-08 00:00:00\\t2002-05-01 00:00:00\\t825 8 Ave SW\\tCalgary\\tAB\\tCanada\\tT2P 2T3\\t+1 (403) 262-3443\\t+1 (403) 262-3322\\tnancy@chinookcorp.com\\n3\\tPeacock\\tJane\\tSales Support Agent\\t2\\t1973-08-29 00:00:00\\t2002-04-01 00:00:00\\t1111 6 Ave SW\\tCalgary\\tAB\\tCanada\\tT2P 5M5\\t+1 (403) 262-3443\\t+1 (403) 262-6712\\tjane@chinookcorp.com\\n*/\\n\\n\\nCREATE TABLE \"Genre\" (\\n\\t\"GenreId\" INTEGER NOT NULL, \\n\\t\"Name\" NVARCHAR(120), \\n\\tPRIMARY KEY (\"GenreId\")\\n)\\n\\n/*\\n3 rows from Genre table:\\nGenreId\\tName\\n1\\tRock\\n2\\tJazz\\n3\\tMetal\\n*/\\n\\n\\nCREATE TABLE \"Invoice\" (\\n\\t\"InvoiceId\" INTEGER NOT NULL, \\n\\t\"CustomerId\" INTEGER NOT NULL, \\n\\t\"InvoiceDate\" DATETIME NOT NULL, \\n\\t\"BillingAddress\" NVARCHAR(70), \\n\\t\"BillingCity\" NVARCHAR(40), \\n\\t\"BillingState\" NVARCHAR(40), \\n\\t\"BillingCountry\" NVARCHAR(40), \\n\\t\"BillingPostalCode\" NVARCHAR(10), \\n\\t\"Total\" NUMERIC(10, 2) NOT NULL, \\n\\tPRIMARY KEY (\"InvoiceId\"), \\n\\tFOREIGN KEY(\"CustomerId\") REFERENCES \"Customer\" (\"CustomerId\")\\n)\\n\\n/*\\n3 rows from Invoice table:\\nInvoiceId\\tCustomerId\\tInvoiceDate\\tBillingAddress\\tBillingCity\\tBillingState\\tBillingCountry\\tBillingPostalCode\\tTotal\\n1\\t2\\t2009-01-01 00:00:00\\tTheodor-Heuss-Straße 34\\tStuttgart\\tNone\\tGermany\\t70174\\t1.98\\n2\\t4\\t2009-01-02 00:00:00\\tUllevålsveien 14\\tOslo\\tNone\\tNorway\\t0171\\t3.96\\n3\\t8\\t2009-01-03 00:00:00\\tGrétrystraat 63\\tBrussels\\tNone\\tBelgium\\t1000\\t5.94\\n*/\\n\\n\\nCREATE TABLE \"InvoiceLine\" (\\n\\t\"InvoiceLineId\" INTEGER NOT NULL, \\n\\t\"InvoiceId\" INTEGER NOT NULL, \\n\\t\"TrackId\" INTEGER NOT NULL, \\n\\t\"UnitPrice\" NUMERIC(10, 2) NOT NULL, \\n\\t\"Quantity\" INTEGER NOT NULL, \\n\\tPRIMARY KEY (\"InvoiceLineId\"), \\n\\tFOREIGN KEY(\"TrackId\") REFERENCES \"Track\" (\"TrackId\"), \\n\\tFOREIGN KEY(\"InvoiceId\") REFERENCES \"Invoice\" (\"InvoiceId\")\\n)\\n\\n/*\\n3 rows from InvoiceLine table:\\nInvoiceLineId\\tInvoiceId\\tTrackId\\tUnitPrice\\tQuantity\\n1\\t1\\t2\\t0.99\\t1\\n2\\t1\\t4\\t0.99\\t1\\n3\\t2\\t6\\t0.99\\t1\\n*/\\n\\n\\nCREATE TABLE \"MediaType\" (\\n\\t\"MediaTypeId\" INTEGER NOT NULL, \\n\\t\"Name\" NVARCHAR(120), \\n\\tPRIMARY KEY (\"MediaTypeId\")\\n)\\n\\n/*\\n3 rows from MediaType table:\\nMediaTypeId\\tName\\n1\\tMPEG audio file\\n2\\tProtected AAC audio file\\n3\\tProtected MPEG-4 video file\\n*/\\n\\n\\nCREATE TABLE \"Playlist\" (\\n\\t\"PlaylistId\" INTEGER NOT NULL, \\n\\t\"Name\" NVARCHAR(120), \\n\\tPRIMARY KEY (\"PlaylistId\")\\n)\\n\\n/*\\n3 rows from Playlist table:\\nPlaylistId\\tName\\n1\\tMusic\\n2\\tMovies\\n3\\tTV Shows\\n*/\\n\\n\\nCREATE TABLE \"PlaylistTrack\" (\\n\\t\"PlaylistId\" INTEGER NOT NULL, \\n\\t\"TrackId\" INTEGER NOT NULL, \\n\\tPRIMARY KEY (\"PlaylistId\", \"TrackId\"), \\n\\tFOREIGN KEY(\"TrackId\") REFERENCES \"Track\" (\"TrackId\"), \\n\\tFOREIGN KEY(\"PlaylistId\") REFERENCES \"Playlist\" (\"PlaylistId\")\\n)\\n\\n/*\\n3 rows from PlaylistTrack table:\\nPlaylistId\\tTrackId\\n1\\t3402\\n1\\t3389\\n1\\t3390\\n*/\\n\\n\\nCREATE TABLE \"Track\" (\\n\\t\"TrackId\" INTEGER NOT NULL, \\n\\t\"Name\" NVARCHAR(200) NOT NULL, \\n\\t\"AlbumId\" INTEGER, \\n\\t\"MediaTypeId\" INTEGER NOT NULL, \\n\\t\"GenreId\" INTEGER, \\n\\t\"Composer\" NVARCHAR(220), \\n\\t\"Milliseconds\" INTEGER NOT NULL, \\n\\t\"Bytes\" INTEGER, \\n\\t\"UnitPrice\" NUMERIC(10, 2) NOT NULL, \\n\\tPRIMARY KEY (\"TrackId\"), \\n\\tFOREIGN KEY(\"MediaTypeId\") REFERENCES \"MediaType\" (\"MediaTypeId\"), \\n\\tFOREIGN KEY(\"GenreId\") REFERENCES \"Genre\" (\"GenreId\"), \\n\\tFOREIGN KEY(\"AlbumId\") REFERENCES \"Album\" (\"AlbumId\")\\n)\\n\\n/*\\n3 rows from Track table:\\nTrackId\\tName\\tAlbumId\\tMediaTypeId\\tGenreId\\tComposer\\tMilliseconds\\tBytes\\tUnitPrice\\n1\\tFor Those About To Rock (We Salute You)\\t1\\t1\\t1\\tAngus Young, Malcolm Young, Brian Johnson\\t343719\\t11170334\\t0.99\\n2\\tBalls to the Wall\\t2\\t2\\t1\\tNone\\t342562\\t5510424\\t0.99\\n3\\tFast As a Shark\\t3\\t2\\t1\\tF. Baltes, S. Kaufman, U. Dirkscneider & W. Hoffman\\t230619\\t3990994\\t0.99\\n*/'"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_community.utilities import SQLDatabase\n",
    "\n",
    "template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query:\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "db = SQLDatabase.from_uri(\"sqlite:///kecheng/Chinook.db\")\n",
    "db.get_table_info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "4ad999c6-5c2e-4579-9909-87a1b09b56e2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'To find out how many artists are in the database, you can use the `COUNT` function on the `Artist` table.\\n\\n### ✅ SQL Query:\\n```sql\\nSELECT COUNT(*) AS ArtistCount\\nFROM Artist;\\n```\\n\\nThis query will return the total number of artists in the `Artist` table.'"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "def get_schema(_):\n",
    "    return db.get_table_info()\n",
    "\n",
    "sql_response = (\n",
    "    RunnablePassthrough.assign(schema=get_schema)\n",
    "    | prompt\n",
    "    | model.bind(stop=[\"\\nSQLResult:\"])\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "sql_response.invoke({\"question\": \"How many artists are there?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "id": "1958e370-4d92-495a-bbbd-72924289e34f",
   "metadata": {},
   "outputs": [
    {
     "ename": "OperationalError",
     "evalue": "(sqlite3.OperationalError) near \"To\": syntax error\n[SQL: To find out how many artists are present in the `Artist` table, you can use the `COUNT()` function in SQL.\n\n### ✅ SQL Query:\n```sql\nSELECT COUNT(*) AS NumberOfArtists\nFROM Artist;\n```\n\nThis query will return the total number of artists in the `Artist` table.]\n(Background on this error at: https://sqlalche.me/e/20/e3q8)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mOperationalError\u001b[0m                          Traceback (most recent call last)",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1963\u001b[0m, in \u001b[0;36mConnection._exec_single_context\u001b[1;34m(self, dialect, context, statement, parameters)\u001b[0m\n\u001b[0;32m   1962\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m evt_handled:\n\u001b[1;32m-> 1963\u001b[0m         \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdialect\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdo_execute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1964\u001b[0m \u001b[43m            \u001b[49m\u001b[43mcursor\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstr_statement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43meffective_parameters\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\n\u001b[0;32m   1965\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1967\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_has_events \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mengine\u001b[38;5;241m.\u001b[39m_has_events:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\default.py:943\u001b[0m, in \u001b[0;36mDefaultDialect.do_execute\u001b[1;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[0;32m    942\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mdo_execute\u001b[39m(\u001b[38;5;28mself\u001b[39m, cursor, statement, parameters, context\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m--> 943\u001b[0m     \u001b[43mcursor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstatement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[1;31mOperationalError\u001b[0m: near \"To\": syntax error",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[1;31mOperationalError\u001b[0m                          Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[76], line 21\u001b[0m\n\u001b[0;32m     11\u001b[0m prompt_response \u001b[38;5;241m=\u001b[39m ChatPromptTemplate\u001b[38;5;241m.\u001b[39mfrom_template(template)\n\u001b[0;32m     13\u001b[0m full_chain \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m     14\u001b[0m     RunnablePassthrough\u001b[38;5;241m.\u001b[39massign(query\u001b[38;5;241m=\u001b[39msql_response)\u001b[38;5;241m.\u001b[39massign(\n\u001b[0;32m     15\u001b[0m         schema\u001b[38;5;241m=\u001b[39mget_schema,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m     19\u001b[0m     \u001b[38;5;241m|\u001b[39m model\n\u001b[0;32m     20\u001b[0m )\n\u001b[1;32m---> 21\u001b[0m \u001b[43mfull_chain\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mquestion\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mHow many artists are there?\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:3049\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m   3047\u001b[0m                 input_ \u001b[38;5;241m=\u001b[39m context\u001b[38;5;241m.\u001b[39mrun(step\u001b[38;5;241m.\u001b[39minvoke, input_, config, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m   3048\u001b[0m             \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 3049\u001b[0m                 input_ \u001b[38;5;241m=\u001b[39m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   3050\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[0;32m   3051\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\passthrough.py:513\u001b[0m, in \u001b[0;36mRunnableAssign.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m    506\u001b[0m \u001b[38;5;129m@override\u001b[39m\n\u001b[0;32m    507\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21minvoke\u001b[39m(\n\u001b[0;32m    508\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    511\u001b[0m     \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any,\n\u001b[0;32m    512\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any]:\n\u001b[1;32m--> 513\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_invoke, \u001b[38;5;28minput\u001b[39m, config, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:1938\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[1;34m(self, func, input_, config, run_type, serialized, **kwargs)\u001b[0m\n\u001b[0;32m   1934\u001b[0m     child_config \u001b[38;5;241m=\u001b[39m patch_config(config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child())\n\u001b[0;32m   1935\u001b[0m     \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(child_config) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[0;32m   1936\u001b[0m         output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[0;32m   1937\u001b[0m             \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOutput\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m-> 1938\u001b[0m             context\u001b[38;5;241m.\u001b[39mrun(\n\u001b[0;32m   1939\u001b[0m                 call_func_with_variable_args,  \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[0;32m   1940\u001b[0m                 func,\n\u001b[0;32m   1941\u001b[0m                 input_,\n\u001b[0;32m   1942\u001b[0m                 config,\n\u001b[0;32m   1943\u001b[0m                 run_manager,\n\u001b[0;32m   1944\u001b[0m                 \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[0;32m   1945\u001b[0m             ),\n\u001b[0;32m   1946\u001b[0m         )\n\u001b[0;32m   1947\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m   1948\u001b[0m     run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\config.py:429\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[1;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[0;32m    427\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[0;32m    428\u001b[0m     kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[1;32m--> 429\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;28minput\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\passthrough.py:499\u001b[0m, in \u001b[0;36mRunnableAssign._invoke\u001b[1;34m(self, value, run_manager, config, **kwargs)\u001b[0m\n\u001b[0;32m    494\u001b[0m     msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe input to RunnablePassthrough.assign() must be a dict.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    495\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(msg)  \u001b[38;5;66;03m# noqa: TRY004\u001b[39;00m\n\u001b[0;32m    497\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[0;32m    498\u001b[0m     \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mvalue,\n\u001b[1;32m--> 499\u001b[0m     \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mmapper\u001b[38;5;241m.\u001b[39minvoke(\n\u001b[0;32m    500\u001b[0m         value,\n\u001b[0;32m    501\u001b[0m         patch_config(config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child()),\n\u001b[0;32m    502\u001b[0m         \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[0;32m    503\u001b[0m     ),\n\u001b[0;32m    504\u001b[0m }\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:3777\u001b[0m, in \u001b[0;36mRunnableParallel.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m   3772\u001b[0m     \u001b[38;5;28;01mwith\u001b[39;00m get_executor_for_config(config) \u001b[38;5;28;01mas\u001b[39;00m executor:\n\u001b[0;32m   3773\u001b[0m         futures \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m   3774\u001b[0m             executor\u001b[38;5;241m.\u001b[39msubmit(_invoke_step, step, \u001b[38;5;28minput\u001b[39m, config, key)\n\u001b[0;32m   3775\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m key, step \u001b[38;5;129;01min\u001b[39;00m steps\u001b[38;5;241m.\u001b[39mitems()\n\u001b[0;32m   3776\u001b[0m         ]\n\u001b[1;32m-> 3777\u001b[0m         output \u001b[38;5;241m=\u001b[39m {key: future\u001b[38;5;241m.\u001b[39mresult() \u001b[38;5;28;01mfor\u001b[39;00m key, future \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(steps, futures)}\n\u001b[0;32m   3778\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[0;32m   3779\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:3777\u001b[0m, in \u001b[0;36m<dictcomp>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m   3772\u001b[0m     \u001b[38;5;28;01mwith\u001b[39;00m get_executor_for_config(config) \u001b[38;5;28;01mas\u001b[39;00m executor:\n\u001b[0;32m   3773\u001b[0m         futures \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m   3774\u001b[0m             executor\u001b[38;5;241m.\u001b[39msubmit(_invoke_step, step, \u001b[38;5;28minput\u001b[39m, config, key)\n\u001b[0;32m   3775\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m key, step \u001b[38;5;129;01min\u001b[39;00m steps\u001b[38;5;241m.\u001b[39mitems()\n\u001b[0;32m   3776\u001b[0m         ]\n\u001b[1;32m-> 3777\u001b[0m         output \u001b[38;5;241m=\u001b[39m {key: \u001b[43mfuture\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m key, future \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(steps, futures)}\n\u001b[0;32m   3778\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n\u001b[0;32m   3779\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\concurrent\\futures\\_base.py:451\u001b[0m, in \u001b[0;36mFuture.result\u001b[1;34m(self, timeout)\u001b[0m\n\u001b[0;32m    449\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[0;32m    450\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;241m==\u001b[39m FINISHED:\n\u001b[1;32m--> 451\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    453\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_condition\u001b[38;5;241m.\u001b[39mwait(timeout)\n\u001b[0;32m    455\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\concurrent\\futures\\_base.py:403\u001b[0m, in \u001b[0;36mFuture.__get_result\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    401\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception:\n\u001b[0;32m    402\u001b[0m     \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 403\u001b[0m         \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception\n\u001b[0;32m    404\u001b[0m     \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m    405\u001b[0m         \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[0;32m    406\u001b[0m         \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\concurrent\\futures\\thread.py:58\u001b[0m, in \u001b[0;36m_WorkItem.run\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m     55\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m     57\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m---> 58\u001b[0m     result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfn(\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mkwargs)\n\u001b[0;32m     59\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[0;32m     60\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfuture\u001b[38;5;241m.\u001b[39mset_exception(exc)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:3761\u001b[0m, in \u001b[0;36mRunnableParallel.invoke.<locals>._invoke_step\u001b[1;34m(step, input_, config, key)\u001b[0m\n\u001b[0;32m   3755\u001b[0m child_config \u001b[38;5;241m=\u001b[39m patch_config(\n\u001b[0;32m   3756\u001b[0m     config,\n\u001b[0;32m   3757\u001b[0m     \u001b[38;5;66;03m# mark each step as a child run\u001b[39;00m\n\u001b[0;32m   3758\u001b[0m     callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmap:key:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkey\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m),\n\u001b[0;32m   3759\u001b[0m )\n\u001b[0;32m   3760\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(child_config) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[1;32m-> 3761\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   3762\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   3763\u001b[0m \u001b[43m        \u001b[49m\u001b[43minput_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   3764\u001b[0m \u001b[43m        \u001b[49m\u001b[43mchild_config\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   3765\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:4781\u001b[0m, in \u001b[0;36mRunnableLambda.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m   4767\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Invoke this Runnable synchronously.\u001b[39;00m\n\u001b[0;32m   4768\u001b[0m \n\u001b[0;32m   4769\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   4778\u001b[0m \u001b[38;5;124;03m    TypeError: If the Runnable is a coroutine function.\u001b[39;00m\n\u001b[0;32m   4779\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m   4780\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfunc\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[1;32m-> 4781\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\n\u001b[0;32m   4782\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_invoke,\n\u001b[0;32m   4783\u001b[0m         \u001b[38;5;28minput\u001b[39m,\n\u001b[0;32m   4784\u001b[0m         ensure_config(config),\n\u001b[0;32m   4785\u001b[0m         \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[0;32m   4786\u001b[0m     )\n\u001b[0;32m   4787\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCannot invoke a coroutine function synchronously.Use `ainvoke` instead.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m   4788\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(msg)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:1938\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[1;34m(self, func, input_, config, run_type, serialized, **kwargs)\u001b[0m\n\u001b[0;32m   1934\u001b[0m     child_config \u001b[38;5;241m=\u001b[39m patch_config(config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child())\n\u001b[0;32m   1935\u001b[0m     \u001b[38;5;28;01mwith\u001b[39;00m set_config_context(child_config) \u001b[38;5;28;01mas\u001b[39;00m context:\n\u001b[0;32m   1936\u001b[0m         output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[0;32m   1937\u001b[0m             \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOutput\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m-> 1938\u001b[0m             context\u001b[38;5;241m.\u001b[39mrun(\n\u001b[0;32m   1939\u001b[0m                 call_func_with_variable_args,  \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[0;32m   1940\u001b[0m                 func,\n\u001b[0;32m   1941\u001b[0m                 input_,\n\u001b[0;32m   1942\u001b[0m                 config,\n\u001b[0;32m   1943\u001b[0m                 run_manager,\n\u001b[0;32m   1944\u001b[0m                 \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs,\n\u001b[0;32m   1945\u001b[0m             ),\n\u001b[0;32m   1946\u001b[0m         )\n\u001b[0;32m   1947\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m   1948\u001b[0m     run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\config.py:429\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[1;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[0;32m    427\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[0;32m    428\u001b[0m     kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[1;32m--> 429\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;28minput\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\base.py:4639\u001b[0m, in \u001b[0;36mRunnableLambda._invoke\u001b[1;34m(self, input_, run_manager, config, **kwargs)\u001b[0m\n\u001b[0;32m   4637\u001b[0m                 output \u001b[38;5;241m=\u001b[39m chunk\n\u001b[0;32m   4638\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 4639\u001b[0m     output \u001b[38;5;241m=\u001b[39m call_func_with_variable_args(\n\u001b[0;32m   4640\u001b[0m         \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfunc, input_, config, run_manager, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs\n\u001b[0;32m   4641\u001b[0m     )\n\u001b[0;32m   4642\u001b[0m \u001b[38;5;66;03m# If the output is a Runnable, invoke it\u001b[39;00m\n\u001b[0;32m   4643\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(output, Runnable):\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\runnables\\config.py:429\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[1;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[0;32m    427\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m accepts_run_manager(func):\n\u001b[0;32m    428\u001b[0m     kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[1;32m--> 429\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m func(\u001b[38;5;28minput\u001b[39m, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "Cell \u001b[1;32mIn[76], line 16\u001b[0m, in \u001b[0;36m<lambda>\u001b[1;34m(x)\u001b[0m\n\u001b[0;32m      5\u001b[0m template \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\"\"\u001b[39m\u001b[38;5;124mBased on the table schema below, question, sql query, and sql response, write a natural language response:\u001b[39m\n\u001b[0;32m      6\u001b[0m \u001b[38;5;132;01m{schema}\u001b[39;00m\n\u001b[0;32m      7\u001b[0m \n\u001b[0;32m      8\u001b[0m \u001b[38;5;124mQuestion: \u001b[39m\u001b[38;5;132;01m{question}\u001b[39;00m\n\u001b[0;32m      9\u001b[0m \u001b[38;5;124mSQL Query: \u001b[39m\u001b[38;5;132;01m{query}\u001b[39;00m\n\u001b[0;32m     10\u001b[0m \u001b[38;5;124mSQL Response: \u001b[39m\u001b[38;5;132;01m{response}\u001b[39;00m\u001b[38;5;124m\"\"\"\u001b[39m\n\u001b[0;32m     11\u001b[0m prompt_response \u001b[38;5;241m=\u001b[39m ChatPromptTemplate\u001b[38;5;241m.\u001b[39mfrom_template(template)\n\u001b[0;32m     13\u001b[0m full_chain \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m     14\u001b[0m     RunnablePassthrough\u001b[38;5;241m.\u001b[39massign(query\u001b[38;5;241m=\u001b[39msql_response)\u001b[38;5;241m.\u001b[39massign(\n\u001b[0;32m     15\u001b[0m         schema\u001b[38;5;241m=\u001b[39mget_schema,\n\u001b[1;32m---> 16\u001b[0m         response\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mlambda\u001b[39;00m x: \u001b[43mdb\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mquery\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m,\n\u001b[0;32m     17\u001b[0m     )\n\u001b[0;32m     18\u001b[0m     \u001b[38;5;241m|\u001b[39m prompt_response\n\u001b[0;32m     19\u001b[0m     \u001b[38;5;241m|\u001b[39m model\n\u001b[0;32m     20\u001b[0m )\n\u001b[0;32m     21\u001b[0m full_chain\u001b[38;5;241m.\u001b[39minvoke({\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mquestion\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHow many artists are there?\u001b[39m\u001b[38;5;124m\"\u001b[39m})\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\utilities\\sql_database.py:553\u001b[0m, in \u001b[0;36mSQLDatabase.run\u001b[1;34m(self, command, fetch, include_columns, parameters, execution_options)\u001b[0m\n\u001b[0;32m    539\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mrun\u001b[39m(\n\u001b[0;32m    540\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m    541\u001b[0m     command: Union[\u001b[38;5;28mstr\u001b[39m, Executable],\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    546\u001b[0m     execution_options: Optional[Dict[\u001b[38;5;28mstr\u001b[39m, Any]] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m    547\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[\u001b[38;5;28mstr\u001b[39m, Sequence[Dict[\u001b[38;5;28mstr\u001b[39m, Any]], Result[Any]]:\n\u001b[0;32m    548\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"Execute a SQL command and return a string representing the results.\u001b[39;00m\n\u001b[0;32m    549\u001b[0m \n\u001b[0;32m    550\u001b[0m \u001b[38;5;124;03m    If the statement returns rows, a string of the results is returned.\u001b[39;00m\n\u001b[0;32m    551\u001b[0m \u001b[38;5;124;03m    If the statement returns no rows, an empty string is returned.\u001b[39;00m\n\u001b[0;32m    552\u001b[0m \u001b[38;5;124;03m    \"\"\"\u001b[39;00m\n\u001b[1;32m--> 553\u001b[0m     result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    554\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcommand\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfetch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparameters\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexecution_options\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mexecution_options\u001b[49m\n\u001b[0;32m    555\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    557\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m fetch \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcursor\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m    558\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m result\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\utilities\\sql_database.py:518\u001b[0m, in \u001b[0;36mSQLDatabase._execute\u001b[1;34m(self, command, fetch, parameters, execution_options)\u001b[0m\n\u001b[0;32m    516\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    517\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mQuery expression has unknown type: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(command)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m--> 518\u001b[0m cursor \u001b[38;5;241m=\u001b[39m \u001b[43mconnection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    519\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcommand\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    520\u001b[0m \u001b[43m    \u001b[49m\u001b[43mparameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    521\u001b[0m \u001b[43m    \u001b[49m\u001b[43mexecution_options\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mexecution_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    522\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    524\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m cursor\u001b[38;5;241m.\u001b[39mreturns_rows:\n\u001b[0;32m    525\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m fetch \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mall\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1415\u001b[0m, in \u001b[0;36mConnection.execute\u001b[1;34m(self, statement, parameters, execution_options)\u001b[0m\n\u001b[0;32m   1413\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m exc\u001b[38;5;241m.\u001b[39mObjectNotExecutableError(statement) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01merr\u001b[39;00m\n\u001b[0;32m   1414\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 1415\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1416\u001b[0m \u001b[43m        \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1417\u001b[0m \u001b[43m        \u001b[49m\u001b[43mdistilled_parameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1418\u001b[0m \u001b[43m        \u001b[49m\u001b[43mexecution_options\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mNO_OPTIONS\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1419\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\sql\\elements.py:523\u001b[0m, in \u001b[0;36mClauseElement._execute_on_connection\u001b[1;34m(self, connection, distilled_params, execution_options)\u001b[0m\n\u001b[0;32m    521\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m TYPE_CHECKING:\n\u001b[0;32m    522\u001b[0m         \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mself\u001b[39m, Executable)\n\u001b[1;32m--> 523\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mconnection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_clauseelement\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    524\u001b[0m \u001b[43m        \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdistilled_params\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexecution_options\u001b[49m\n\u001b[0;32m    525\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    526\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    527\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m exc\u001b[38;5;241m.\u001b[39mObjectNotExecutableError(\u001b[38;5;28mself\u001b[39m)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1637\u001b[0m, in \u001b[0;36mConnection._execute_clauseelement\u001b[1;34m(self, elem, distilled_parameters, execution_options)\u001b[0m\n\u001b[0;32m   1625\u001b[0m compiled_cache: Optional[CompiledCacheType] \u001b[38;5;241m=\u001b[39m execution_options\u001b[38;5;241m.\u001b[39mget(\n\u001b[0;32m   1626\u001b[0m     \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcompiled_cache\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mengine\u001b[38;5;241m.\u001b[39m_compiled_cache\n\u001b[0;32m   1627\u001b[0m )\n\u001b[0;32m   1629\u001b[0m compiled_sql, extracted_params, cache_hit \u001b[38;5;241m=\u001b[39m elem\u001b[38;5;241m.\u001b[39m_compile_w_cache(\n\u001b[0;32m   1630\u001b[0m     dialect\u001b[38;5;241m=\u001b[39mdialect,\n\u001b[0;32m   1631\u001b[0m     compiled_cache\u001b[38;5;241m=\u001b[39mcompiled_cache,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1635\u001b[0m     linting\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdialect\u001b[38;5;241m.\u001b[39mcompiler_linting \u001b[38;5;241m|\u001b[39m compiler\u001b[38;5;241m.\u001b[39mWARN_LINTING,\n\u001b[0;32m   1636\u001b[0m )\n\u001b[1;32m-> 1637\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_context\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1638\u001b[0m \u001b[43m    \u001b[49m\u001b[43mdialect\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1639\u001b[0m \u001b[43m    \u001b[49m\u001b[43mdialect\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecution_ctx_cls\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_init_compiled\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1640\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcompiled_sql\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1641\u001b[0m \u001b[43m    \u001b[49m\u001b[43mdistilled_parameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1642\u001b[0m \u001b[43m    \u001b[49m\u001b[43mexecution_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1643\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcompiled_sql\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1644\u001b[0m \u001b[43m    \u001b[49m\u001b[43mdistilled_parameters\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1645\u001b[0m \u001b[43m    \u001b[49m\u001b[43melem\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1646\u001b[0m \u001b[43m    \u001b[49m\u001b[43mextracted_params\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1647\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcache_hit\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcache_hit\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1648\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1649\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m has_events:\n\u001b[0;32m   1650\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdispatch\u001b[38;5;241m.\u001b[39mafter_execute(\n\u001b[0;32m   1651\u001b[0m         \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1652\u001b[0m         elem,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1656\u001b[0m         ret,\n\u001b[0;32m   1657\u001b[0m     )\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1842\u001b[0m, in \u001b[0;36mConnection._execute_context\u001b[1;34m(self, dialect, constructor, statement, parameters, execution_options, *args, **kw)\u001b[0m\n\u001b[0;32m   1840\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exec_insertmany_context(dialect, context)\n\u001b[0;32m   1841\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m-> 1842\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_exec_single_context\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1843\u001b[0m \u001b[43m        \u001b[49m\u001b[43mdialect\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstatement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\n\u001b[0;32m   1844\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1982\u001b[0m, in \u001b[0;36mConnection._exec_single_context\u001b[1;34m(self, dialect, context, statement, parameters)\u001b[0m\n\u001b[0;32m   1979\u001b[0m     result \u001b[38;5;241m=\u001b[39m context\u001b[38;5;241m.\u001b[39m_setup_result_proxy()\n\u001b[0;32m   1981\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m-> 1982\u001b[0m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_handle_dbapi_exception\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1983\u001b[0m \u001b[43m        \u001b[49m\u001b[43me\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstr_statement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43meffective_parameters\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcursor\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\n\u001b[0;32m   1984\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1986\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m result\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:2351\u001b[0m, in \u001b[0;36mConnection._handle_dbapi_exception\u001b[1;34m(self, e, statement, parameters, cursor, context, is_sub_exec)\u001b[0m\n\u001b[0;32m   2349\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m should_wrap:\n\u001b[0;32m   2350\u001b[0m     \u001b[38;5;28;01massert\u001b[39;00m sqlalchemy_exception \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m-> 2351\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m sqlalchemy_exception\u001b[38;5;241m.\u001b[39mwith_traceback(exc_info[\u001b[38;5;241m2\u001b[39m]) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01me\u001b[39;00m\n\u001b[0;32m   2352\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m   2353\u001b[0m     \u001b[38;5;28;01massert\u001b[39;00m exc_info[\u001b[38;5;241m1\u001b[39m] \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\base.py:1963\u001b[0m, in \u001b[0;36mConnection._exec_single_context\u001b[1;34m(self, dialect, context, statement, parameters)\u001b[0m\n\u001b[0;32m   1961\u001b[0m                 \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m   1962\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m evt_handled:\n\u001b[1;32m-> 1963\u001b[0m         \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdialect\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdo_execute\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1964\u001b[0m \u001b[43m            \u001b[49m\u001b[43mcursor\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstr_statement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43meffective_parameters\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcontext\u001b[49m\n\u001b[0;32m   1965\u001b[0m \u001b[43m        \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1967\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_has_events \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mengine\u001b[38;5;241m.\u001b[39m_has_events:\n\u001b[0;32m   1968\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdispatch\u001b[38;5;241m.\u001b[39mafter_cursor_execute(\n\u001b[0;32m   1969\u001b[0m         \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1970\u001b[0m         cursor,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1974\u001b[0m         context\u001b[38;5;241m.\u001b[39mexecutemany,\n\u001b[0;32m   1975\u001b[0m     )\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\sqlalchemy\\engine\\default.py:943\u001b[0m, in \u001b[0;36mDefaultDialect.do_execute\u001b[1;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[0;32m    942\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mdo_execute\u001b[39m(\u001b[38;5;28mself\u001b[39m, cursor, statement, parameters, context\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[1;32m--> 943\u001b[0m     \u001b[43mcursor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstatement\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparameters\u001b[49m\u001b[43m)\u001b[49m\n",
      "\u001b[1;31mOperationalError\u001b[0m: (sqlite3.OperationalError) near \"To\": syntax error\n[SQL: To find out how many artists are present in the `Artist` table, you can use the `COUNT()` function in SQL.\n\n### ✅ SQL Query:\n```sql\nSELECT COUNT(*) AS NumberOfArtists\nFROM Artist;\n```\n\nThis query will return the total number of artists in the `Artist` table.]\n(Background on this error at: https://sqlalche.me/e/20/e3q8)"
     ]
    }
   ],
   "source": [
    "def run_query(query):\n",
    "    print(query)\n",
    "    return db.run(query)\n",
    "\n",
    "template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query: {query}\n",
    "SQL Response: {response}\"\"\"\n",
    "prompt_response = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "full_chain = (\n",
    "    RunnablePassthrough.assign(query=sql_response).assign(\n",
    "        schema=get_schema,\n",
    "        response=lambda x: db.run(x[\"query\"]),\n",
    "    )\n",
    "    | prompt_response\n",
    "    | model\n",
    ")\n",
    "full_chain.invoke({\"question\": \"How many artists are there?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7272b0c5-8b9e-4394-bc63-ac01cb8b7344",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "result:  \n",
      "----------------------------\n"
     ]
    }
   ],
   "source": [
    "# 自定义输出解析器\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import (\n",
    "    ChatPromptTemplate,\n",
    ")\n",
    "from langchain_experimental.utilities import PythonREPL\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "template = \"\"\"根据用户需求帮助用户编写完整、可独立运行的python代码。 \n",
    "\n",
    "要求：\n",
    "1. 代码必须是完整的，包含所有必要的函数定义\n",
    "2. 代码必须能够独立运行，不需要额外的导入或定义\n",
    "3. 只需要返回makedown格式的python代码\n",
    "\n",
    "用户需求：{input}\"\"\"\n",
    "prompt = ChatPromptTemplate.from_messages([(\"system\", template), (\"human\", \"{input}\")])\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "# 自定义输出解析，只返回python代码\n",
    "def _sanitize_output(text: str):\n",
    "    if \"```python\" in text:\n",
    "        _, after = text.split(\"```python\")\n",
    "        return after.split(\"```\")[0]\n",
    "    else:\n",
    "        # 如果没有代码块，尝试直接返回\n",
    "        return text.strip()\n",
    "\n",
    "# 定义链\n",
    "chain = prompt | model | StrOutputParser() | _sanitize_output | PythonREPL().run\n",
    "\n",
    "try:\n",
    "    result = chain.invoke({\"input\": \"从10里面循环找出所有质数\"})\n",
    "    print(\"result: \", result)\n",
    "    print(\"----------------------------\")\n",
    "except Exception as e:\n",
    "    print(f\"Error: {e}\")\n",
    "    print(\"error---------------------\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "0e463d27-2837-47b4-ba43-da902900f371",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='There are 275 artists in the database.' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 2397, 'total_tokens': 2408, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-bc97134d-153a-9929-a80b-88e6220840d7', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--476563b1-2165-471a-9527-9c13e2d09e24-0' usage_metadata={'input_tokens': 2397, 'output_tokens': 11, 'total_tokens': 2408, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_community.utilities import SQLDatabase\n",
    "import re\n",
    "\n",
    "template = \"\"\"Based on the table schema below, write a SQL query that would answer the user's question.\n",
    "Write ONLY the SQL query, without any additional explanations or markdown formatting.\n",
    "\n",
    "Table schema:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query:\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "db = SQLDatabase.from_uri(\"sqlite:///kecheng/Chinook.db\")\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "def get_schema(_):\n",
    "    return db.get_table_info()\n",
    "\n",
    "def extract_sql_query(text):\n",
    "    \"\"\"从模型输出中提取纯粹的SQL查询\"\"\"\n",
    "    # 尝试查找代码块中的SQL\n",
    "    code_block_match = re.search(r'```sql\\s*(.*?)\\s*```', text, re.DOTALL | re.IGNORECASE)\n",
    "    if code_block_match:\n",
    "        return code_block_match.group(1).strip()\n",
    "    \n",
    "    # 如果没有代码块，尝试查找以SELECT/INSERT/UPDATE等开头的语句\n",
    "    sql_match = re.search(r'(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP).*?;', text, re.DOTALL | re.IGNORECASE)\n",
    "    if sql_match:\n",
    "        return sql_match.group(0).strip()\n",
    "    \n",
    "    # 如果都找不到，返回原始文本（让数据库报错）\n",
    "    return text.strip()\n",
    "\n",
    "sql_response = (\n",
    "    RunnablePassthrough.assign(schema=get_schema)\n",
    "    | prompt\n",
    "    | model.bind(stop=[\"\\nSQLResult:\"])\n",
    "    | StrOutputParser()\n",
    "    | extract_sql_query  # 添加SQL提取步骤\n",
    ")\n",
    "\n",
    "template = \"\"\"Based on the table schema below, question, sql query, and sql response, write a natural language response:\n",
    "{schema}\n",
    "\n",
    "Question: {question}\n",
    "SQL Query: {query}\n",
    "SQL Response: {response}\"\"\"\n",
    "prompt_response = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "full_chain = (\n",
    "    RunnablePassthrough.assign(query=sql_response).assign(\n",
    "        schema=get_schema,\n",
    "        response=lambda x: db.run(x[\"query\"]),\n",
    "    )\n",
    "    | prompt_response\n",
    "    | model\n",
    ")\n",
    "\n",
    "try:\n",
    "    result = full_chain.invoke({\"question\": \"How many artists are there?\"})\n",
    "    print(result)\n",
    "except Exception as e:\n",
    "    print(f\"Error: {e}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "19be2be8-50a4-4d2d-a1b3-93fdbe089fef",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\27727\\AppData\\Local\\Temp\\ipykernel_8320\\1877225242.py:22: LangChainDeprecationWarning: Please see the migration guide at: https://python.langchain.com/docs/versions/migrating_memory/\n",
      "  memory = ConversationBufferMemory(return_messages=True)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'history': []}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# LCEL:memory添加方式\n",
    "from operator import itemgetter\n",
    "\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"你是一个乐于助人的机器人\"),\n",
    "        MessagesPlaceholder(variable_name=\"history\"),\n",
    "        (\"human\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "memory = ConversationBufferMemory(return_messages=True)\n",
    "memory.load_memory_variables({})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "21d10e96-fd2e-40c2-902b-898082461f4b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你好，Tomie！有什么可以帮助你的吗？😊', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 28, 'total_tokens': 39, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-c2100b80-0b4a-9b48-bba6-cae68e259831', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--10b72bd2-7dd3-4435-81d1-ab45d67fdc08-0', usage_metadata={'input_tokens': 28, 'output_tokens': 11, 'total_tokens': 39, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 增加一条链\n",
    "chain = (\n",
    "    RunnablePassthrough.assign(\n",
    "        history=RunnableLambda(memory.load_memory_variables) | itemgetter(\"history\")\n",
    "    )\n",
    "    | prompt\n",
    "    | model\n",
    ")\n",
    "\n",
    "inputs = {\"input\": \"你好我是tomie\"}\n",
    "response = chain.invoke(inputs)\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "68ac0979-a715-4f01-9aee-cae3083db44a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'history': [HumanMessage(content='你好我是tomie', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='你好，Tomie！有什么可以帮助你的吗？😊', additional_kwargs={}, response_metadata={})]}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#保存记忆\n",
    "memory.save_context(inputs, {\"output\": response.content})\n",
    "memory.load_memory_variables({})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "ecb76772-3ea9-43d0-a377-feb23e06178f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='你刚刚告诉我你的名字是 Tomie。😊', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 54, 'total_tokens': 64, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-84ba6547-e85a-9897-9f6e-feb5fb6c8e92', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--e2ac1878-89ed-4e70-8cf0-81c04d0a4be9-0', usage_metadata={'input_tokens': 54, 'output_tokens': 10, 'total_tokens': 64, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "inputs = {\"input\": \"我叫什么名字?\"}\n",
    "response = chain.invoke(inputs)\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "97f17ecf-95d6-4a6c-a844-c904b216b4d8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='中国建都时间最长的城市是：\\n\\n> ✅ **西安**\\n\\n---\\n\\n### 📌 简明答案：\\n\\n- **城市**：西安（古称长安、镐京等）\\n- **建都朝代**：西周、秦、西汉、新莽、东汉（短暂）、西晋（短暂）、前赵、前秦、后秦、西魏、北周、隋、唐等\\n- **建都时间跨度**：约公元前1046年（西周）至公元907年（唐灭亡）\\n- **累计建都时间**：**超过1000年**\\n- **称号**：中国历史上**建都朝代最多、建都时间最长、影响力最大的古都之一**\\n\\n---\\n\\n### 🏛️ 西安作为都城的地位\\n\\n- 是**汉唐时期**中国的政治、经济、文化中心\\n- 是古代**丝绸之路的起点**\\n- 唐代长安城是当时世界上**最大、最繁华的城市之一**\\n- 被誉为“**十三朝古都**”\\n\\n---\\n\\n### 🔁 对比其他著名古都\\n\\n| 城市 | 建都时间（约） | 主要朝代 | 备注 |\\n|------|----------------|-----------|------|\\n| 西安 | 超过1000年     | 西周、秦、西汉、隋、唐等 | 建都时间最长 |\\n| 洛阳 | 约900年        | 东周、东汉、北魏、唐等 | 文化底蕴深厚 |\\n| 北京 | 约800年        | 金、元、明、清等 | 近代首都 |\\n| 南京 | 约450年        | 东吴、东晋、南朝、明初等 | 六朝古都 |\\n| 杭州 | 约200年        | 吴越、南宋等 | 南宋都城 |\\n\\n---\\n\\n### ✅ 总结：\\n\\n**西安是中国历史上建都时间最长的城市**，拥有超过**1000年的建都历史**，在中国古代文明发展中具有极其重要的地位。', additional_kwargs={}, response_metadata={'token_usage': {'completion_tokens': 473, 'prompt_tokens': 1270, 'total_tokens': 1743, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--f4469319-f629-431e-9eed-6bdd93e6b2f0-0')"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用Redis来实现长时记忆\n",
    "# pip install redis\n",
    "\n",
    "from typing import Optional\n",
    "\n",
    "from langchain_community.chat_message_histories import RedisChatMessageHistory\n",
    "from langchain_community.chat_models import ChatOpenAI\n",
    "from langchain_core.chat_history import BaseChatMessageHistory\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\"system\", \"你是一个擅长{ability}的助手\"),\n",
    "        MessagesPlaceholder(variable_name=\"history\"),\n",
    "        (\"human\", \"{question}\"),\n",
    "    ]\n",
    ")\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "chain = prompt | model\n",
    "\n",
    "chain_with_history = RunnableWithMessageHistory(\n",
    "    chain,\n",
    "    #使用redis存储聊天记录\n",
    "    lambda session_id: RedisChatMessageHistory(session_id, url=\"redis://localhost:6379/0\"),\n",
    "    input_messages_key=\"question\",\n",
    "    history_messages_key=\"history\",\n",
    ")\n",
    "\n",
    "#每次调用都会保存聊天记录，需要有对应的session_id\n",
    "chain_with_history.invoke(\n",
    "    {\"ability\": \"历史\", \"question\": \"中国建都时间最长的城市是哪个?\"},\n",
    "    config={\"configurable\": {\"session_id\": \"tomiezhang\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "9025c436-65ae-4136-a01b-9e4838eed11e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='**西安的建都历史超过1000年**。\\n\\n---\\n\\n### 📅 详细说明：\\n\\n西安作为都城的历史始于**西周**，终于**唐朝**，中间经历了多个朝代在此建都。虽然不是连续作为都城，但**累计建都时间超过1000年**。\\n\\n---\\n\\n### 🏛️ 各朝代在西安建都的时间（简表）\\n\\n| 朝代       | 建都时间（大致）         | 建都年数估算 |\\n|------------|--------------------------|--------------|\\n| 西周       | 公元前1046年—前771年     | 约275年      |\\n| 秦（统一后） | 公元前221年—前207年      | 约14年       |\\n| 西汉       | 公元前202年—公元8年      | 约210年      |\\n| 新莽       | 公元9年—公元23年         | 约15年       |\\n| 东汉（短暫） | 公元190年—195年          | 约5年        |\\n| 西晋（短暫） | 公元313年—316年          | 约3年        |\\n| 十六国时期（前赵、前秦、后秦） | 319年—431年 | 约112年     |\\n| 北朝（西魏、北周） | 534年—581年         | 约47年       |\\n| 隋         | 581年—618年              | 约37年       |\\n| 唐         | 618年—907年              | 约289年      |\\n\\n> ✅ **总计建都时间：约1000年以上**\\n\\n---\\n\\n### 📝 补充说明：\\n\\n- 西安古称**长安**、**镐京**、**大兴**等。\\n- 被誉为“**十三朝古都**”，是中国历史上**建都朝代最多、建都时间最长、影响力最大的都城之一**。\\n- 唐代的长安城是当时世界上**规模最大、最繁华的城市之一**。\\n\\n---\\n\\n### ✅ 总结：\\n\\n> **西安的建都历史超过1000年**，是中国建都时间最长的城市。', additional_kwargs={}, response_metadata={'token_usage': {'completion_tokens': 552, 'prompt_tokens': 1760, 'total_tokens': 2312, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--887adf21-9472-4e65-a7b3-88fc975e3dbc-0')"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain_with_history.invoke(\n",
    "    {\"ability\": \"历史\", \"question\": \"它有多少年建都历史？\"},\n",
    "    config={\"configurable\": {\"session_id\": \"tomiezhang\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "1b3c08a5-d18a-47a7-aed1-e61c2a949791",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langsmith\\client.py:272: LangSmithMissingAPIKeyWarning: API key must be provided when using hosted LangSmith API\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你好！有什么可以帮助你的吗？\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '你好', 'output': '你好！有什么可以帮助你的吗？'}"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain import hub\n",
    "from langchain.agents import load_tools\n",
    "from langchain.agents import create_openai_functions_agent #不同的agent有不同的创建方式\n",
    "from langchain.agents import AgentExecutor\n",
    "#创建LLM \n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "#定义agent的prompt\n",
    "#https://smith.langchain.com/hub/hwchase17/openai-functions-agent\n",
    "prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
    "#定义工具,加载预制的工具,注意有的工具需要提供LLM\n",
    "tools = load_tools([\"llm-math\"], llm=llm)\n",
    "#创建agent\n",
    "agent = create_openai_functions_agent(llm, tools, prompt)\n",
    "#定义agent的执行器，这里注意与老版本的不同\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True  # 添加错误处理\n",
    "                              )\n",
    "agent_executor.invoke({\"input\": \"你好\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "6c6a4e64-9cbf-4374-b12b-f009c7ae21c0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[StructuredTool(name='simpleSearch', description='当需要了解最新的天气信息的时候才会使用这个工具。', args_schema=<class 'langchain_core.utils.pydantic.simpleSearch'>, func=<function simpleSearch at 0x000001C9B253ADD0>)]\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langsmith\\client.py:272: LangSmithMissingAPIKeyWarning: API key must be provided when using hosted LangSmith API\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prompt： input_variables=['agent_scratchpad', 'input', 'tools'] input_types={} partial_variables={'chat_history': ''} metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'xml-agent-convo', 'lc_hub_commit_hash': '00f6b7470fa25a24eef6e4e3c1e44ba07189f3e91c4d987223ad232490673be8'} messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['agent_scratchpad', 'chat_history', 'input', 'tools'], input_types={}, partial_variables={}, template=\"You are a helpful assistant. Help the user answer any questions.\\n\\nYou have access to the following tools:\\n\\n{tools}\\n\\nIn order to use a tool, you can use <tool></tool> and <tool_input></tool_input> tags. You will then get back a response in the form <observation></observation>\\nFor example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond:\\n\\n<tool>search</tool><tool_input>weather in SF</tool_input>\\n<observation>64 degrees</observation>\\n\\nWhen you are done, respond with a final answer between <final_answer></final_answer>. For example:\\n\\n<final_answer>The weather in SF is 64 degrees</final_answer>\\n\\nBegin!\\n\\nPrevious Conversation:\\n{chat_history}\\n\\nQuestion: {input}\\n{agent_scratchpad}\"), additional_kwargs={})]\n",
      "agent:  first={\n",
      "  input: RunnableLambda(...),\n",
      "  agent_scratchpad: RunnableLambda(...)\n",
      "} middle=[ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], input_types={}, partial_variables={'chat_history': '', 'tools': 'simpleSearch: 当需要了解最新的天气信息的时候才会使用这个工具。'}, metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'xml-agent-convo', 'lc_hub_commit_hash': '00f6b7470fa25a24eef6e4e3c1e44ba07189f3e91c4d987223ad232490673be8'}, messages=[HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['agent_scratchpad', 'chat_history', 'input', 'tools'], input_types={}, partial_variables={}, template=\"You are a helpful assistant. Help the user answer any questions.\\n\\nYou have access to the following tools:\\n\\n{tools}\\n\\nIn order to use a tool, you can use <tool></tool> and <tool_input></tool_input> tags. You will then get back a response in the form <observation></observation>\\nFor example, if you have a tool called 'search' that could run a google search, in order to search for the weather in SF you would respond:\\n\\n<tool>search</tool><tool_input>weather in SF</tool_input>\\n<observation>64 degrees</observation>\\n\\nWhen you are done, respond with a final answer between <final_answer></final_answer>. For example:\\n\\n<final_answer>The weather in SF is 64 degrees</final_answer>\\n\\nBegin!\\n\\nPrevious Conversation:\\n{chat_history}\\n\\nQuestion: {input}\\n{agent_scratchpad}\"), additional_kwargs={})]), RunnableBinding(bound=ChatOpenAI(client=<openai.resources.chat.completions.completions.Completions object at 0x000001C9B258AA10>, async_client=<openai.resources.chat.completions.completions.AsyncCompletions object at 0x000001C9B258B880>, root_client=<openai.OpenAI object at 0x000001C9B258AB30>, root_async_client=<openai.AsyncOpenAI object at 0x000001C9B258AAD0>, model_name='qwen-plus', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********'), openai_api_base='https://dashscope.aliyuncs.com/compatible-mode/v1'), kwargs={'stop': ['</tool_input>', '</final_answer>']}, config={}, config_factories=[])] last=XMLAgentOutputParser()\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m<tool>simpleSearch</tool><tool_input>Beijing weather today</tool_input\u001b[0m\u001b[36;1m\u001b[1;3m晴朗,32摄氏度,无风\u001b[0m\u001b[32;1m\u001b[1;3m<final_answer>北京今天的天气晴朗，32摄氏度，无风。</\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '北京今天的天气如何?', 'output': '北京今天的天气晴朗，32摄氏度，无风。</'}"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"Agent\n",
    "中间步骤处理\n",
    "提示词\n",
    "模型配置(停止符必要的话)\n",
    "输出解析器\n",
    "\"\"\"\n",
    "from langchain import hub\n",
    "from langchain.agents import AgentExecutor, tool\n",
    "from langchain.agents.output_parsers import XMLAgentOutputParser\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "#配置模型\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "#可用工具\n",
    "@tool\n",
    "def simpleSearch(query: str) -> str:\n",
    "    \"\"\"当需要了解最新的天气信息的时候才会使用这个工具。\"\"\"\n",
    "    return \"晴朗,32摄氏度,无风\"\n",
    "tool_list = [simpleSearch]\n",
    "print(tool_list)\n",
    "\n",
    "#提示词模版\n",
    "# https://smith.langchain.com/hub\n",
    "# Get the prompt to use - you can modify this!\n",
    "prompt = hub.pull(\"hwchase17/xml-agent-convo\")\n",
    "print(\"prompt：\", prompt)\n",
    "\n",
    "# 中间步骤，实现一个log\n",
    "def convert_intermediate_steps(intermediate_steps):\n",
    "    log = \"\"\n",
    "    for action, observation in intermediate_steps:\n",
    "        log += (\n",
    "            f\"<tool>{action.tool}</tool><tool_input>{action.tool_input}\"\n",
    "            f\"</tool_input><observation>{observation}</observation>\"\n",
    "        )\n",
    "    return log\n",
    "\n",
    "#将工具列表插入到模版中\n",
    "def convert_tools(tools):\n",
    "    return \"\\n\".join([f\"{tool.name}: {tool.description}\" for tool in tools])\n",
    "\n",
    "# 定义agent\n",
    "agent = (\n",
    "    {\n",
    "        \"input\": lambda x: x[\"input\"],\n",
    "        \"agent_scratchpad\": lambda x: convert_intermediate_steps(\n",
    "            x[\"intermediate_steps\"]\n",
    "        ),\n",
    "    }\n",
    "    | prompt.partial(tools=convert_tools(tool_list))\n",
    "    | model.bind(stop=[\"</tool_input>\", \"</final_answer>\"])\n",
    "    | XMLAgentOutputParser()\n",
    ")\n",
    "print(\"agent: \", agent)\n",
    "\n",
    "#执行agent\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tool_list, verbose=True, handle_parsing_errors=True  # 添加错误处理\n",
    "                              )\n",
    "\n",
    "agent_executor.invoke({\"input\": \"北京今天的天气如何?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "84b054ca-66de-410d-9627-ba16318256da",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loaded 1 documents\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "Document(id='6804a9ea-96b4-4dfa-8d52-cbb4abe1a050', metadata={'source': 'https://docs.smith.langchain.com/user_guide', 'title': '\\uf8ffü¶úÔ∏è\\uf8ffüõ†Ô∏è LangSmith', 'language': 'en'}, page_content='\\uf8ffü¶úÔ∏è\\uf8ffüõ†Ô∏è LangSmith\\n\\n\\n\\n\\n\\n\\n\\n\\nSkip to main contentOur new LangChain Academy Course Deep Research with LangGraph is now live! Enroll for free.API ReferenceRESTPythonJS/TSSearchRegionUSEUGo to AppPage Not FoundWe could not find what you were looking for.Head back to our main docs page or use the search bar to find the page you need.CommunityLangChain ForumTwitterGitHubDocs CodeLangSmith SDKPythonJS/TSMoreHomepageBlogLangChain Python DocsLangChain JS/TS DocsCopyright ¬© 2025 LangChain, Inc.')"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#RAG增强生成\n",
    "from langchain.text_splitter import RecursiveCharacterTextSplitter\n",
    "from langchain_community.document_loaders import WebBaseLoader\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_community.embeddings import DashScopeEmbeddings  # 使用阿里云的嵌入模型\n",
    "\n",
    "loader = WebBaseLoader(\"https://docs.smith.langchain.com/user_guide\")\n",
    "docs = loader.load()\n",
    "print(f\"Loaded {len(docs)} documents\")\n",
    "documents = RecursiveCharacterTextSplitter(\n",
    "    chunk_size=1000, chunk_overlap=200\n",
    ").split_documents(docs)\n",
    "# 使用阿里云的嵌入模型\n",
    "embeddings = DashScopeEmbeddings(\n",
    "    model=\"text-embedding-v1\",  # 阿里云的嵌入模型\n",
    "    dashscope_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\"\n",
    ")\n",
    "vector = FAISS.from_documents(documents, embeddings)\n",
    "retriever = vector.as_retriever()\n",
    "\n",
    "#搜索匹配文档块\n",
    "retriever.get_relevant_documents(\"如何debug?\")[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "0d8d3300-0eff-4bd8-ade7-435feebaff5b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Tool(name='langsmith_search', description='搜索有关 LangSmith 的信息。关于LangSmith的任何问题，你一定要使用这个工具！', args_schema=<class 'langchain_core.tools.retriever.RetrieverInput'>, func=functools.partial(<function _get_relevant_documents at 0x000001C9AC107C70>, retriever=VectorStoreRetriever(tags=['FAISS', 'DashScopeEmbeddings'], vectorstore=<langchain_community.vectorstores.faiss.FAISS object at 0x000001C9B2559570>, search_kwargs={}), document_prompt=PromptTemplate(input_variables=['page_content'], input_types={}, partial_variables={}, template='{page_content}'), document_separator='\\n\\n', response_format='content'), coroutine=functools.partial(<function _aget_relevant_documents at 0x000001C9AAA4D120>, retriever=VectorStoreRetriever(tags=['FAISS', 'DashScopeEmbeddings'], vectorstore=<langchain_community.vectorstores.faiss.FAISS object at 0x000001C9B2559570>, search_kwargs={}), document_prompt=PromptTemplate(input_variables=['page_content'], input_types={}, partial_variables={}, template='{page_content}'), document_separator='\\n\\n', response_format='content'))"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#把检索器加入到工具中\n",
    "from langchain.tools.retriever import create_retriever_tool\n",
    "retriever_tool = create_retriever_tool(\n",
    "    retriever,\n",
    "    \"langsmith_search\",\n",
    "    \"搜索有关 LangSmith 的信息。关于LangSmith的任何问题，你一定要使用这个工具！\",\n",
    ")\n",
    "retriever_tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "58af93f8-0822-4e88-ad62-c8df284d75d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[StructuredTool(name='simpleSearch', description='当需要了解最新的天气信息的时候才会使用这个工具。', args_schema=<class 'langchain_core.utils.pydantic.simpleSearch'>, func=<function simpleSearch at 0x000001C9B253ADD0>),\n",
       " Tool(name='langsmith_search', description='搜索有关 LangSmith 的信息。关于LangSmith的任何问题，你一定要使用这个工具！', args_schema=<class 'langchain_core.tools.retriever.RetrieverInput'>, func=functools.partial(<function _get_relevant_documents at 0x000001C9AC107C70>, retriever=VectorStoreRetriever(tags=['FAISS', 'DashScopeEmbeddings'], vectorstore=<langchain_community.vectorstores.faiss.FAISS object at 0x000001C9B2559570>, search_kwargs={}), document_prompt=PromptTemplate(input_variables=['page_content'], input_types={}, partial_variables={}, template='{page_content}'), document_separator='\\n\\n', response_format='content'), coroutine=functools.partial(<function _aget_relevant_documents at 0x000001C9AAA4D120>, retriever=VectorStoreRetriever(tags=['FAISS', 'DashScopeEmbeddings'], vectorstore=<langchain_community.vectorstores.faiss.FAISS object at 0x000001C9B2559570>, search_kwargs={}), document_prompt=PromptTemplate(input_variables=['page_content'], input_types={}, partial_variables={}, template='{page_content}'), document_separator='\\n\\n', response_format='content'))]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#可用工具集\n",
    "tools = [simpleSearch, retriever_tool]\n",
    "tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "c2c4fe27-4e4b-495a-8b15-6cd856bfd799",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langsmith\\client.py:272: LangSmithMissingAPIKeyWarning: API key must be provided when using hosted LangSmith API\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prompt.messages:  [SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], input_types={}, partial_variables={}, template='You are a helpful assistant'), additional_kwargs={}), MessagesPlaceholder(variable_name='chat_history', optional=True), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], input_types={}, partial_variables={}, template='{input}'), additional_kwargs={}), MessagesPlaceholder(variable_name='agent_scratchpad')]\n",
      "agent:  first=RunnableAssign(mapper={\n",
      "  agent_scratchpad: RunnableLambda(lambda x: format_to_openai_function_messages(x['intermediate_steps']))\n",
      "}) middle=[ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], optional_variables=['chat_history'], input_types={'chat_history': list[typing.Annotated[typing.Union[typing.Annotated[langchain_core.messages.ai.AIMessage, Tag(tag='ai')], typing.Annotated[langchain_core.messages.human.HumanMessage, Tag(tag='human')], typing.Annotated[langchain_core.messages.chat.ChatMessage, Tag(tag='chat')], typing.Annotated[langchain_core.messages.system.SystemMessage, Tag(tag='system')], typing.Annotated[langchain_core.messages.function.FunctionMessage, Tag(tag='function')], typing.Annotated[langchain_core.messages.tool.ToolMessage, Tag(tag='tool')], typing.Annotated[langchain_core.messages.ai.AIMessageChunk, Tag(tag='AIMessageChunk')], typing.Annotated[langchain_core.messages.human.HumanMessageChunk, Tag(tag='HumanMessageChunk')], typing.Annotated[langchain_core.messages.chat.ChatMessageChunk, Tag(tag='ChatMessageChunk')], typing.Annotated[langchain_core.messages.system.SystemMessageChunk, Tag(tag='SystemMessageChunk')], typing.Annotated[langchain_core.messages.function.FunctionMessageChunk, Tag(tag='FunctionMessageChunk')], typing.Annotated[langchain_core.messages.tool.ToolMessageChunk, Tag(tag='ToolMessageChunk')]], FieldInfo(annotation=NoneType, required=True, discriminator=Discriminator(discriminator=<function _get_type at 0x000001C9A80728C0>, custom_error_type=None, custom_error_message=None, custom_error_context=None))]], 'agent_scratchpad': list[typing.Annotated[typing.Union[typing.Annotated[langchain_core.messages.ai.AIMessage, Tag(tag='ai')], typing.Annotated[langchain_core.messages.human.HumanMessage, Tag(tag='human')], typing.Annotated[langchain_core.messages.chat.ChatMessage, Tag(tag='chat')], typing.Annotated[langchain_core.messages.system.SystemMessage, Tag(tag='system')], typing.Annotated[langchain_core.messages.function.FunctionMessage, Tag(tag='function')], typing.Annotated[langchain_core.messages.tool.ToolMessage, Tag(tag='tool')], typing.Annotated[langchain_core.messages.ai.AIMessageChunk, Tag(tag='AIMessageChunk')], typing.Annotated[langchain_core.messages.human.HumanMessageChunk, Tag(tag='HumanMessageChunk')], typing.Annotated[langchain_core.messages.chat.ChatMessageChunk, Tag(tag='ChatMessageChunk')], typing.Annotated[langchain_core.messages.system.SystemMessageChunk, Tag(tag='SystemMessageChunk')], typing.Annotated[langchain_core.messages.function.FunctionMessageChunk, Tag(tag='FunctionMessageChunk')], typing.Annotated[langchain_core.messages.tool.ToolMessageChunk, Tag(tag='ToolMessageChunk')]], FieldInfo(annotation=NoneType, required=True, discriminator=Discriminator(discriminator=<function _get_type at 0x000001C9A80728C0>, custom_error_type=None, custom_error_message=None, custom_error_context=None))]]}, partial_variables={'chat_history': []}, metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'openai-functions-agent', 'lc_hub_commit_hash': 'a1655024b06afbd95d17449f21316291e0726f13dcfaf990cc0d18087ad689a5'}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], input_types={}, partial_variables={}, template='You are a helpful assistant'), additional_kwargs={}), MessagesPlaceholder(variable_name='chat_history', optional=True), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], input_types={}, partial_variables={}, template='{input}'), additional_kwargs={}), MessagesPlaceholder(variable_name='agent_scratchpad')]), RunnableBinding(bound=ChatOpenAI(client=<openai.resources.chat.completions.completions.Completions object at 0x000001C9B258BA60>, async_client=<openai.resources.chat.completions.completions.AsyncCompletions object at 0x000001C9B258A500>, root_client=<openai.OpenAI object at 0x000001C9B258BB80>, root_async_client=<openai.AsyncOpenAI object at 0x000001C9B25881F0>, model_name='qwen-plus', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********'), openai_api_base='https://dashscope.aliyuncs.com/compatible-mode/v1'), kwargs={'functions': [{'name': 'simpleSearch', 'description': '当需要了解最新的天气信息的时候才会使用这个工具。', 'parameters': {'properties': {'query': {'type': 'string'}}, 'required': ['query'], 'type': 'object'}}, {'name': 'langsmith_search', 'description': '搜索有关 LangSmith 的信息。关于LangSmith的任何问题，你一定要使用这个工具！', 'parameters': {'properties': {'query': {'description': 'query to look up in retriever', 'type': 'string'}}, 'required': ['query'], 'type': 'object'}}]}, config={}, config_factories=[])] last=OpenAIFunctionsAgentOutputParser()\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mHello! How can I assist you today?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'hi!', 'output': 'Hello! How can I assist you today?'}"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#定义模型\n",
    "from langchain_openai import ChatOpenAI\n",
    "#从hub中获取模版\n",
    "from langchain import hub\n",
    "#创建agent\n",
    "from langchain.agents import create_openai_functions_agent\n",
    "#创建agent执行器AgentExecutor\n",
    "from langchain.agents import AgentExecutor\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "# 一个最简单的模版,带记忆\n",
    "prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
    "print(\"prompt.messages: \", prompt.messages)\n",
    "\n",
    "agent = create_openai_functions_agent(llm, tools, prompt)\n",
    "print(\"agent: \", agent)\n",
    "\n",
    "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True  # 添加错误处理\n",
    "                              )\n",
    "#执行\n",
    "agent_executor.invoke({\"input\": \"hi!\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "bdf8a741-8dbd-46b9-a6dc-9605fdbb3e0e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"langsmith如何帮助做', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3m\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'langsmith如何帮助做项目测试?',\n",
       " 'output': 'Agent stopped due to iteration limit or time limit.'}"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_executor.invoke({\"input\": \"langsmith如何帮助做项目测试?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "32f0b4d7-ac99-4cd0-9a7e-ca9035bdfe1a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `simpleSearch` with `{'query': '北京 今天的天气'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[36;1m\u001b[1;3m晴朗,32摄氏度,无风\u001b[0m\u001b[32;1m\u001b[1;3m北京今天晴朗，气温32摄氏度，无风。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '北京今天的天气如何?', 'output': '北京今天晴朗，气温32摄氏度，无风。'}"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#实时信息提问\n",
    "agent_executor.invoke({\"input\": \"北京今天的天气如何?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "f7e34179-4160-4c61-909e-43123fd916a4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m截至目前，我们的对话内容主要围绕以下几个方面：\n",
      "\n",
      "1. **工具使用说明**：我向你介绍了可用的工具，包括 `simpleSearch`（用于查询最新天气信息）和 `langsmith_search`（用于搜索 LangSmith 相关信息）。\n",
      "\n",
      "2. **对话历史回顾**：你询问了我们目前为止聊了什么，我对此进行了总结。\n",
      "\n",
      "如果你有其他具体问题或需要进一步了解的内容，欢迎随时告诉我！\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '截止目前我们都聊了什么?',\n",
       " 'output': '截至目前，我们的对话内容主要围绕以下几个方面：\\n\\n1. **工具使用说明**：我向你介绍了可用的工具，包括 `simpleSearch`（用于查询最新天气信息）和 `langsmith_search`（用于搜索 LangSmith 相关信息）。\\n\\n2. **对话历史回顾**：你询问了我们目前为止聊了什么，我对此进行了总结。\\n\\n如果你有其他具体问题或需要进一步了解的内容，欢迎随时告诉我！'}"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#前面的交互都不带记忆\n",
    "agent_executor.invoke({\"input\": \"截止目前我们都聊了什么?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "cd90cc15-ecb7-4bd0-803a-c00766011f7d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mHello, Bob! How can I assist you today?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'hi! 我叫bob',\n",
       " 'chat_history': [],\n",
       " 'output': 'Hello, Bob! How can I assist you today?'}"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#交互时添加记忆\n",
    "agent_executor.invoke({\"input\": \"hi! 我叫bob\", \"chat_history\": []})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "1d48aef9-95b5-4b0c-bf99-7d13a69a6262",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你叫 Bob。\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'chat_history': [HumanMessage(content='hi! 我叫bob', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='你好，Bob！很高兴认识你。有什么我可以帮助你的吗？', additional_kwargs={}, response_metadata={})],\n",
       " 'input': '我叫什么名字?',\n",
       " 'output': '你叫 Bob。'}"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#手动构造记忆数据\n",
    "from langchain_core.messages import AIMessage, HumanMessage\n",
    "agent_executor.invoke(\n",
    "    {\n",
    "        \"chat_history\": [\n",
    "            HumanMessage(content=\"hi! 我叫bob\"),\n",
    "            AIMessage(content=\"你好，Bob！很高兴认识你。有什么我可以帮助你的吗？\"),\n",
    "        ],\n",
    "        \"input\": \"我叫什么名字?\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "id": "cfd17f9b-ac17-4640-89cf-1be737322599",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mHello, Tomie! How can I assist you today?\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'hi! 我是tomie',\n",
       " 'chat_history': [],\n",
       " 'output': 'Hello, Tomie! How can I assist you today?'}"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#使用RunnableWithMessageHistory自动构造记忆数据\n",
    "from langchain_community.chat_message_histories import ChatMessageHistory\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "message_history = ChatMessageHistory()\n",
    "\n",
    "agent_with_chat_history = RunnableWithMessageHistory(\n",
    "    agent_executor,\n",
    "    #注意此处session_id没有用到，因为我们没用类似redis的存储,只是用了一个变量\n",
    "    lambda session_id: message_history,\n",
    "    input_messages_key=\"input\",\n",
    "    history_messages_key=\"chat_history\",\n",
    ")\n",
    "\n",
    "#调用方式\n",
    "agent_with_chat_history.invoke(\n",
    "    {\"input\": \"hi! 我是tomie\"},\n",
    "    #注意此处session_id没有用到，因为我们没用类似redis的存储,只是用了一个变量\n",
    "    config={\"configurable\": {\"session_id\": \"foo_001\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "e39d53d6-d23f-4b5f-a35f-b8e3d2e56932",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m你叫 Tomie。😊\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '我叫什么名字?',\n",
       " 'chat_history': [HumanMessage(content='hi! 我是tomie', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='Hello, Tomie! How can I assist you today?', additional_kwargs={}, response_metadata={})],\n",
       " 'output': '你叫 Tomie。😊'}"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#调用方式\n",
    "agent_with_chat_history.invoke(\n",
    "    {\"input\": \"我叫什么名字?\"},\n",
    "    #注意此处session_id没有用到，因为我们没用类似redis的存储,只是用了一个变量\n",
    "    config={\"configurable\": {\"session_id\": \"foo_001\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "fa5900e0-f477-414b-b705-4b86422e4fd3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m到目前为止，我们聊了以下内容：\n",
      "\n",
      "1. 你介绍了自己，名字是 Tomie。\n",
      "2. 我向你打招呼并表示很高兴认识你。\n",
      "3. 你问我你叫什么名字，我回答了你的名字。\n",
      "4. 现在你在问我们目前为止聊了什么。\n",
      "\n",
      "如果你有任何其他问题或想继续话题，请告诉我！😊\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': '截止目前我们都聊了什么?',\n",
       " 'chat_history': [HumanMessage(content='hi! 我是tomie', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='Hello, Tomie! How can I assist you today?', additional_kwargs={}, response_metadata={}),\n",
       "  HumanMessage(content='我叫什么名字?', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='你叫 Tomie。😊', additional_kwargs={}, response_metadata={})],\n",
       " 'output': '到目前为止，我们聊了以下内容：\\n\\n1. 你介绍了自己，名字是 Tomie。\\n2. 我向你打招呼并表示很高兴认识你。\\n3. 你问我你叫什么名字，我回答了你的名字。\\n4. 现在你在问我们目前为止聊了什么。\\n\\n如果你有任何其他问题或想继续话题，请告诉我！😊'}"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#调用方式\n",
    "agent_with_chat_history.invoke(\n",
    "    {\"input\": \"截止目前我们都聊了什么?\"},\n",
    "    #注意此处session_id没有用到，因为我们没用类似redis的存储,只是用了一个变量\n",
    "    config={\"configurable\": {\"session_id\": \"foo_001\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "id": "987b04ca-159e-45d0-a0d2-668101d98ad0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3mCould not parse tool input: {'arguments': '{\"query\": \"如何使用 Lang', 'name': 'langsmith_search'} because the `arguments` is not valid JSON.\n",
      "For troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/OUTPUT_PARSING_FAILURE \u001b[0mInvalid or incomplete response\u001b[32;1m\u001b[1;3m\n",
      "Invoking: `langsmith_search` with `{'query': '如何使用 LangSmith'}`\n",
      "\n",
      "\n",
      "\u001b[0m\u001b[33;1m\u001b[1;3mü¶úÔ∏èüõ†Ô∏è LangSmith\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "Skip to main contentOur new LangChain Academy Course Deep Research with LangGraph is now live! Enroll for free.API ReferenceRESTPythonJS/TSSearchRegionUSEUGo to AppPage Not FoundWe could not find what you were looking for.Head back to our main docs page or use the search bar to find the page you need.CommunityLangChain ForumTwitterGitHubDocs CodeLangSmith SDKPythonJS/TSMoreHomepageBlogLangChain Python DocsLangChain JS/TS DocsCopyright ¬© 2025 LangChain, Inc.\u001b[0m\u001b[32;1m\u001b[1;3m有关 LangSmith 的使用方法，我目前检索到了相关资源，但具体内容未能成功解析。你可以访问 LangSmith 的官方文档页面来获取详细的使用指南：\n",
      "\n",
      "- **LangChain 主页**: [LangChain Homepage](https://python.langchain.com/)\n",
      "- **LangSmith SDK 文档**: 查看 LangChain 的 Python 或 JavaScript/TypeScript 文档，里面会涵盖 LangSmith 的具体使用方法。\n",
      "- **GitHub**: 如果你更喜欢直接查看代码示例，LangChain 的 GitHub 页面可能提供相关示例和指南。\n",
      "\n",
      "如果你有更具体的问题，可以告诉我，我会尽力帮助你！😊\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'input': 'LangSmith如何使用?',\n",
       " 'chat_history': [HumanMessage(content='hi! 我是tomie', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='Hello, Tomie! How can I assist you today?', additional_kwargs={}, response_metadata={}),\n",
       "  HumanMessage(content='我叫什么名字?', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='你叫 Tomie。😊', additional_kwargs={}, response_metadata={}),\n",
       "  HumanMessage(content='截止目前我们都聊了什么?', additional_kwargs={}, response_metadata={}),\n",
       "  AIMessage(content='到目前为止，我们聊了以下内容：\\n\\n1. 你介绍了自己，名字是 Tomie。\\n2. 我向你打招呼并表示很高兴认识你。\\n3. 你问我你叫什么名字，我回答了你的名字。\\n4. 现在你在问我们目前为止聊了什么。\\n\\n如果你有任何其他问题或想继续话题，请告诉我！😊', additional_kwargs={}, response_metadata={})],\n",
       " 'output': '有关 LangSmith 的使用方法，我目前检索到了相关资源，但具体内容未能成功解析。你可以访问 LangSmith 的官方文档页面来获取详细的使用指南：\\n\\n- **LangChain 主页**: [LangChain Homepage](https://python.langchain.com/)\\n- **LangSmith SDK 文档**: 查看 LangChain 的 Python 或 JavaScript/TypeScript 文档，里面会涵盖 LangSmith 的具体使用方法。\\n- **GitHub**: 如果你更喜欢直接查看代码示例，LangChain 的 GitHub 页面可能提供相关示例和指南。\\n\\n如果你有更具体的问题，可以告诉我，我会尽力帮助你！😊'}"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#调用方式\n",
    "agent_with_chat_history.invoke(\n",
    "    {\"input\": \"LangSmith如何使用?\"},\n",
    "    #注意此处session_id没有用到，因为我们没用类似redis的存储,只是用了一个变量\n",
    "    config={\"configurable\": {\"session_id\": \"foo_001\"}},\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "823809fa-81ef-4197-9ae8-80c837f5d5a7",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "rag_learn",
   "language": "python",
   "name": "rag_learn"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
